package edu.pku.sei.autounit.reflect;

public abstract class AbstractType implements Type{
	
	
	final public boolean isAssignableFrom(Type type){
		if(type == null)
			return false;

		else if(this instanceof Class){
			if(type instanceof Class){
				if(getFullName().equals(type.getFullName())){
					Type[] out = ((Class) type).getContexts();
					Type[] in = ((Class) this).getContexts();
					if(in.length == 0 || out.length == 0)
						return true;
					for(int i = 0;i<out.length;i++)
							if(!out[i].equals(in[i]))
								return false;
					return true;
				}
				else{
					Class s = ((Class) type).getSuperClass();
					if(s != null)
						return isAssignableFrom(s);
					else
						return false;
				}
			}
			else if(type instanceof Primitive){
				if(type.getFullName().equals("boolean")
						&&getFullName().equals("java.lang.Boolean"))
					return true;
				else if(type.getFullName().equals("byte")
						&&getFullName().equals("java.lang.Byte"))
					return true;
				else if(type.getFullName().equals("char")
						&&getFullName().equals("java.lang.Byte"))
					return true;
				else if(type.getFullName().equals("char")
						&&getFullName().equals("java.lang.Character"))
					return true;
				else if(type.getFullName().equals("byte")
						&&getFullName().equals("java.lang.Character"))
					return true;
				else if(type.getFullName().equals("short")
						&&getFullName().equals("java.lang.Short"))
					return true;
				else if(type.getFullName().equals("int")
						&&getFullName().equals("java.lang.Integer"))
					return true;
				else if(type.getFullName().equals("char")
						&&getFullName().equals("java.lang.Integer"))
					return true;
				else if(type.getFullName().equals("byte")
						&&getFullName().equals("java.lang.Integer"))
					return true;
				else if(type.getFullName().equals("long")
						&&getFullName().equals("java.lang.Long"))
					return true;
				else if(type.getFullName().equals("float")
						&&getFullName().equals("java.lang.Float"))
					return true;
				else if(type.getFullName().equals("double")
						&&getFullName().equals("java.lang.Double"))
					return true;
				else 
					return false;
			}
			else 
				return false;
		}
		
		else if(this instanceof Array){
			if(type instanceof Array)
				return ((Array) this).getElementType().isAssignableFrom
					(((Array) this).getElementType());
			else
				return false;
		}
		
		else if(this instanceof Enum){
			return getFullName().equals(type.getFullName());
		}
		
		else if(this instanceof Interface){
			if(type instanceof Class){
				Class c = (Class)type;
				for(Interface i:c.getInterfaces()){
					if(isAssignableFrom(i))
						return true;
				}
				Class s = c.getSuperClass();
				if(s != null)
					return isAssignableFrom(s);
				else
					return false;
			}
			else if(type instanceof Interface){
				Interface inter = (Interface) type;
				
				if(getFullName().equals(inter.getFullName())){
					Type[] out = inter.getContexts();
					Type[] in = ((Interface) this).getContexts();
					if(in.length == 0 || out.length == 0)
						return true;
					for(int i = 0;i<out.length;i++)
						if(!out[i].equals(in[i]))
							return false;
					return true;
				}
				else{
					for(Interface i:inter.getInterfaces()){
						if(this.isAssignableFrom(i))
							return true;
					}
					return false;
				}
			}
			else
				return false;
		}
		
		else if(this instanceof Primitive){
			if(getFullName().equals("boolean")){
				if( type.getFullName().equals("boolean")||
					type.getFullName().equals("java.lang.Boolean"))
					return true;
				else
					return false;
			}
			else if(getFullName().equals("byte")){
				if( type.getFullName().equals("byte")||
					type.getFullName().equals("char")||
					type.getFullName().equals("java.lang.Byte"))
					return true;
				else
					return false;
			}
			else if(getFullName().equals("char")){
				if( type.getFullName().equals("char")||
					type.getFullName().equals("byte")||
					type.getFullName().equals("java.lang.Character"))
					return true;
				else
					return false;
			}
			else if(getFullName().equals("short")){
				if( type.getFullName().equals("short")||
					type.getFullName().equals("char")||
					type.getFullName().equals("byte")||
					type.getFullName().equals("java.lang.Short"))
					return true;
				else
					return false;
			}
			else if(getFullName().equals("int")){
				if( type.getFullName().equals("int")||
					type.getFullName().equals("short")||
					type.getFullName().equals("char")||
					type.getFullName().equals("byte")||
					type.getFullName().equals("java.lang.Integer"))
					return true;
				else
					return false;
			}
			else if(getFullName().equals("long")){
				if( type.getFullName().equals("long")||
					type.getFullName().equals("int")||
					type.getFullName().equals("short")||
					type.getFullName().equals("char")||
					type.getFullName().equals("byte")||
					type.getFullName().equals("java.lang.Long"))
					return true;
				else
					return false;
			}
			else if(getFullName().equals("float")){
				if( type.getFullName().equals("float")||
					type.getFullName().equals("long")||
					type.getFullName().equals("int")||
					type.getFullName().equals("short")||
					type.getFullName().equals("char")||
					type.getFullName().equals("byte")||
					type.getFullName().equals("java.lang.Float"))
					return true;
				else
					return false;
			}
			else if(getFullName().equals("double")){
				if( type.getFullName().equals("double")||
					type.getFullName().equals("float")||
					type.getFullName().equals("long")||
					type.getFullName().equals("int")||
					type.getFullName().equals("short")||
					type.getFullName().equals("char")||
					type.getFullName().equals("byte")||
					type.getFullName().equals("java.lang.Double"))
					return true;
				else
					return false;
			}
			else
				return false;
		}
		
		else if(this instanceof TypeVariable){
			Type [] bs = ((TypeVariable) this).getBounds();
			for(Type t:bs)
				if(t.isAssignableFrom(type))
					return true;
			return false;
		}
		else
			return false;
	}
	
	
	final public boolean equals(Object o) {
		if(o == null)
			return false;
		if(!(o instanceof Type))
			return false;
		Type type = (Type) o;
		
		if(this instanceof Class){
			if(type instanceof Class){
				if(getFullName().equals(type.getFullName())){
					Type[] out = ((Class) type).getContexts();
					Type[] in = ((Class) this).getContexts();
					if(in.length != out.length)
						return false;
					for(int i = 0;i<out.length;i++)
							if(!out[i].equals(in[i]))
								return false;
					return true;
				}
				else{
					return false;
				}
			}
			else 
				return false;
		}
		
		else if(this instanceof Array){
			if(type instanceof Array)
				return ((Array) this).getElementType().equals
					(((Array) this).getElementType());
			else
				return false;
		}
		
		else if(this instanceof Enum){
			return getFullName().equals(type.getFullName());
		}
		
		else if(this instanceof Interface){
			if(type instanceof Interface){
				Interface inter = (Interface) type;
				
				if(getFullName().equals(inter.getFullName())){
					Type[] out = inter.getContexts();
					Type[] in = ((Interface) this).getContexts();
					if(in.length != out.length)
						return false;
					for(int i = 0;i<out.length;i++)
						if(!out[i].equals(in[i]))
							return false;
					return true;
				}
				else
					return false;
			}
			else
				return false;
		}
		
		else if(this instanceof Primitive){
			return this.getFullName().equals(type.getFullName());
		}
		
		else if(this instanceof TypeVariable){
			if(type instanceof TypeVariable)
				return type.getFullName().equals(getFullName());
			else
				return false;
		}
		
		else if(this instanceof WildcardType){
			if(type instanceof WildcardType){
				WildcardType in = (WildcardType) this;
				WildcardType out = (WildcardType) type;
				Type l = in.getLowerBound();
				Type u = in.getUpperBound();
				if(l != null&&!l.equals(out.getLowerBound()))
					return false;
				else if(l == null && out.getLowerBound() != null)
					return false;
				if(u != null&&!u.equals(out.getUpperBound()))
					return false;
				else if(u == null && out.getUpperBound() != null)
					return false;
				return true;
			}
			else
				return false;
		}
		else
			return this.getFullName().equals(type.getFullName());
	}
	
	final public String toString(){
		return getFullName();
	}
}
